/********************************************************************************
 *    Copyright (C) 2014 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH    *
 *                                                                              *
 *              This software is distributed under the terms of the             * 
 *              GNU Lesser General Public Licence (LGPL) version 3,             *  
 *                  copied verbatim in the file "LICENSE"                       *
 ********************************************************************************/
#include "NewDetector.h"

#include "NewDetectorPoint.h"
#include "NewDetectorGeo.h"
#include "NewDetectorGeoPar.h"

#include "FairVolume.h"
#include "FairGeoVolume.h"
#include "FairGeoNode.h"
#include "FairRootManager.h"
#include "FairGeoLoader.h"
#include "FairGeoInterface.h"
#include "FairRun.h"
#include "FairRuntimeDb.h"
#include "MyProjDetectorList.h"
#include "MyProjStack.h"

#include "TClonesArray.h"
#include "TVirtualMC.h"
#include "TGeoManager.h"
#include "TGeoBBox.h"
#include "TGeoCompositeShape.h"
#include "TGeoTube.h"
#include "TGeoMaterial.h"
#include "TGeoMedium.h"

#include <iostream>
using std::cout;
using std::endl;

NewDetector::NewDetector()
  : FairDetector("NewDetector", kTRUE, kNewDetector),
    fTrackID(-1),
    fVolumeID(-1),
    fPos(),
    fMom(),
    fTime(-1.),
    fLength(-1.),
    fELoss(-1),
    fNewDetectorPointCollection(new TClonesArray("NewDetectorPoint"))
{
}

NewDetector::NewDetector(const char* name, Bool_t active)
  : FairDetector(name, active, kNewDetector),
    fTrackID(-1),
    fVolumeID(-1),
    fPos(),
    fMom(),
    fTime(-1.),
    fLength(-1.),
    fELoss(-1),
    fNewDetectorPointCollection(new TClonesArray("NewDetectorPoint"))
{
}

NewDetector::NewDetector(const NewDetector& right)
  : FairDetector(right),
    fTrackID(-1),
    fVolumeID(-1),
    fPos(),
    fMom(),
    fTime(-1.),
    fLength(-1.),
    fELoss(-1),
    fNewDetectorPointCollection(new TClonesArray("NewDetectorPoint"))
{
}

NewDetector::~NewDetector()
{
  if (fNewDetectorPointCollection) {
    fNewDetectorPointCollection->Delete();
    delete fNewDetectorPointCollection;
  }
}

void NewDetector::Initialize()
{
  /**
   * WORKAROUND needed for Geant4 in MT mode
   * Call AddSensitiveVolume for sensitive volumes in order to fill
   * thread-local FairModule::svList.
   */
  DefineSensitiveVolumes();

  FairDetector::Initialize();
  FairRuntimeDb* rtdb= FairRun::Instance()->GetRuntimeDb();
  NewDetectorGeoPar* par=(NewDetectorGeoPar*)(rtdb->getContainer("NewDetectorGeoPar"));
}

Bool_t  NewDetector::ProcessHits(FairVolume* vol)
{
  /** This method is called from the MC stepping */

  //Set parameters at entrance of volume. Reset ELoss.
  if ( TVirtualMC::GetMC()->IsTrackEntering() ) {
    fELoss  = 0.;
    fTime   = TVirtualMC::GetMC()->TrackTime() * 1.0e09;
    fLength = TVirtualMC::GetMC()->TrackLength();
    TVirtualMC::GetMC()->TrackPosition(fPos);
    TVirtualMC::GetMC()->TrackMomentum(fMom);
  }

  // Sum energy loss for all steps in the active volume
  fELoss += TVirtualMC::GetMC()->Edep();

  // Create NewDetectorPoint at exit of active volume
  if ( TVirtualMC::GetMC()->IsTrackExiting()    ||
       TVirtualMC::GetMC()->IsTrackStop()       ||
       TVirtualMC::GetMC()->IsTrackDisappeared()   ) {
    fTrackID  = TVirtualMC::GetMC()->GetStack()->GetCurrentTrackNumber();
    fVolumeID = vol->getMCid();
    if (fELoss == 0. ) { return kFALSE; }
    AddHit(fTrackID, fVolumeID, TVector3(fPos.X(),  fPos.Y(),  fPos.Z()),
           TVector3(fMom.Px(), fMom.Py(), fMom.Pz()), fTime, fLength,
           fELoss);

    // Increment number of NewDetector det points in TParticle
    MyProjStack* stack = (MyProjStack*) TVirtualMC::GetMC()->GetStack();
    stack->AddPoint(kNewDetector);
  }

  return kTRUE;
}

void NewDetector::EndOfEvent()
{

  LOG(info) << "NewDetector: " << fNewDetectorPointCollection->GetEntriesFast()
            << " points registered in this event";

  fNewDetectorPointCollection->Clear();

}



void NewDetector::Register()
{

  /** This will create a branch in the output tree called
      NewDetectorPoint, setting the last parameter to kFALSE means:
      this collection will not be written to the file, it will exist
      only during the simulation.
  */

  if ( ! gMC->IsMT() ) {
    FairRootManager::Instance()->Register("NewDetectorPoint", "NewDetector",
                                          fNewDetectorPointCollection, kTRUE);
  } else {
    FairRootManager::Instance()->RegisterAny("NewDetectorPoint",
                                             fNewDetectorPointCollection, kTRUE);
  }

}


TClonesArray* NewDetector::GetCollection(Int_t iColl) const
{
  if (iColl == 0) { return fNewDetectorPointCollection; }
  else { return NULL; }
}

void NewDetector::Reset()
{
  fNewDetectorPointCollection->Clear();
}

void NewDetector::ConstructGeometry()
{
	TString fileName=GetGeometryFileName();
	if (fileName.EndsWith(".geo")) {
		LOG(info)<<"Constructing geometry from ASCII file "<<fileName;
		ConstructASCIIGeometry();
	} else if (fileName.EndsWith(".root")) {
		LOG(info)<<"Constructing geometry from ROOT file "<<fileName;
		ConstructRootGeometry();
	} else {
		LOG(fatal) << "Geometry format not supported.";
	}

}

NewDetectorPoint* NewDetector::AddHit(Int_t trackID, Int_t detID,
                                      TVector3 pos, TVector3 mom,
                                      Double_t time, Double_t length,
                                      Double_t eLoss)
{
  TClonesArray& clref = *fNewDetectorPointCollection;
  Int_t size = clref.GetEntriesFast();
  return new(clref[size]) NewDetectorPoint(trackID, detID, pos, mom,
         time, length, eLoss);
}

FairModule* NewDetector::CloneModule() const
{
  return new NewDetector(*this);
}

void NewDetector::DefineSensitiveVolumes()
{
  TObjArray* volumes = gGeoManager->GetListOfVolumes();
  TIter next(volumes);
  TGeoVolume* volume;
  while ( ( volume = static_cast<TGeoVolume*>(next()) ) ) {
    if ( CheckIfSensitive(volume->GetName()) ) {
      LOG(debug2)<<"Sensitive Volume "<< volume->GetName();
      AddSensitiveVolume(volume);
    }
  }
}

Bool_t NewDetector::CheckIfSensitive(std::string name)
{
  if(TString(name).Contains("Det"))
  {
    return kTRUE;
  }
  return kFALSE;
}

ClassImp(NewDetector)
